using System;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using System.Xml.Xsl;

/*	-----------------------------------------------------------------------	
	Copyright:	umlungu consulting (pty) ltd
	Author:		Alan Benington
	Started:	2007-10-27
	Status:		release
	Version:	2.0.0
	Build:		20071123
	License:	GNU General Public License
	-----------------------------------------------------------------------	*/

/*	-----------------------------------------------------------------------	
	Development Notes:
	==================
	20071027:	Start of .Net2 version from EmailX.1.0.2
	20071123:	Integration of features from blogX.x_email
	-----------------------------------------------------------------------	*/

namespace umlungu.engineX.emailX {
	/// <summary>Type of email</summary>
	public enum x_emailtype {
		/// <summary>Text email</summary>
		text = 1,
		/// <summary>Xml (text) email</summary>
		xml = 2,
		/// <summary>Html email</summary>
		html = 3
	}
	/// <summary>Separator type between form fields</summary>
	public enum x_emailfields {
		/// <summary>Text email</summary>
		semicolon = 1,
		/// <summary>Xml (text) email</summary>
		comma = 2,
		/// <summary>Html email</summary>
		space = 3
	}
	/// <summary>
	/// Description of the classX class.
	/// <para>Additional information about the class</para>
	/// </summary>
	public class x_email {
		#region Invisible properties
		private x_config config;
		#endregion

		#region Constants
		private const string DEFAULT_TO = "customer@umlungu.com";
		private const string DEFAULT_BCC = "";
		private const string DEFAULT_FROM = "EngineX@umlungu.com";
		private const string DEFAULT_SUBJECT = "EngineX contact message";
		private const string DEFAULT_SERVER = "localhost";
		private const x_emailtype DEFAULT_TYPE = x_emailtype.text;
		#endregion

		#region Visible properties
		private string server = "localhost";
		/// <summary>The SMTP server to be used to send a message</summary>
		/// <value>IP or named address</value>
		public string Server {
			get { return server; }
			set { server = value; }
		}
		private x_emailtype type;
		/// <summary>The type of email to be sent</summary>
		/// <value>An x_emailtype enumeration obejct</value>
		public x_emailtype Type {
			get { return type; }
			set { type = value; }
		}
		private string to;
		/// <summary>The destination email address</summary>
		/// <value>valid email address</value>
		public string To {
			get { return to; }
			set { to = value; }
		}
		private string from;
		/// <summary>The originating email address</summary>
		/// <value>valid email address</value>
		public string From {
			get { return from; }
			set { from = value; }
		}
		private string cc;
		/// <summary>The cc email address</summary>
		/// <value>valid email address</value>
		public string Cc {
			get { return cc; }
			set { cc = value; }
		}
		private string bcc;
		/// <summary>The bcc email address</summary>
		/// <value>valid email address</value>
		public string Bcc {
			get { return bcc; }
			set { bcc = value; }
		}
		private string subject;
		/// <summary>The email subject</summary>
		/// <value>valid email address</value>
		public string Subject {
			get { return subject; }
			set { subject = value; }
		}
		private string message;
		/// <summary>The email message</summary>
		/// <value>The message string</value>
		public string Message {
			get { return message; }
			set { message = value; }
		}
		private string altmessage;
		/// <summary>The email message (alternative text)</summary>
		/// <value>Plain text message string</value>
		public string AltMessage {
			get { return altmessage; }
			set { altmessage = value; }
		}
		private XslCompiledTransform htmlTemplate;
		/// <summary></summary>
		/// <value></value>
		public XslCompiledTransform HtmlTemplate {
			get { return htmlTemplate; }
			set { htmlTemplate = value; }
		}
		private string subjectPrefix;
		/// <summary>A prefix to the email subject</summary>
		/// <value>to be pre-pended to all email subjects</value>
		public string SubjectPrefix {
			get { return subjectPrefix; }
			set { subjectPrefix = value; }
		}

		/// <summary>Checks and sets the destination email address</summary>
		/// <value>valid email address</value>
		public string ToCheck {
			set { to = isValidEmail(value); }
		}
		#endregion

		#region Constructors/Destructors
		/// <overloads>Constructor</overloads>
		/// <summary>Default constructor</summary>
		public x_email() {
			initialise();
		}
		/// <summary>Specify which smtp server to use</summary>
		/// <param name="thisserver">Brief description of the parameter</param>
		public x_email(string thisserver) {
			initialise();
			server = thisserver;
		}
		/// <summary>Constructor description</summary>
		/// <param name="emailTo">The email 'to' address</param>
		/// <param name="emailFrom">The email 'from' address</param>
		/// <param name="emailSubject">The email body</param>
		public x_email(string emailTo, string emailFrom, string emailSubject) {
			initialise();
			to = emailTo;
			from = emailFrom;
			subject = emailSubject;
		}
		#endregion

		#region Public sending methods (from BlogX.x_email)
		/// <overloads>Sends the email</overloads>
		/// <summary>Default. Assumes to, from, subject and message already set</summary>
		public void Send() {
			if (type == x_emailtype.xml || type == x_emailtype.text) {
				SmtpClient smtp = new SmtpClient();
				smtp.Send(from, to, subject, message);
			} else {
				Send(new MailMessage());
			}
		}
		/// <summary>MailMessage supplied (with attachments, if required). Assumes to, from, subject and message already set</summary>
		public void Send(MailMessage thisMail) {
			thisMail.From = new MailAddress(from);
			thisMail.To.Add(new MailAddress(to));
			thisMail.Subject = subject;
			if (altmessage == null) {
				thisMail.IsBodyHtml = (type == x_emailtype.xml || type == x_emailtype.text) ? false : true;
				thisMail.Body = message;
			} else {
				thisMail.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(altmessage, null, MediaTypeNames.Text.Plain));
				thisMail.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(message, null, MediaTypeNames.Text.Html));
			}
			if (cc != null && cc != "") {
				thisMail.CC.Add(new MailAddress(cc));
			}
			if (bcc != null && bcc != "") {
				thisMail.Bcc.Add(new MailAddress(bcc));
			}
			setSmtp().Send(thisMail);
		}
		/// <summary>Send a text message</summary>
		/// <param name="msg">The email message body</param>
		public void Send(string msg) {
			message = msg;
			Send();
		}
		/// <summary>Send the form fields defined in the request</summary>
		/// <param name="request">The http request object</param>
		public void Send(HttpRequest request) {
			message = formatMessage(request, null);
			Send();
		}
		/// <summary>Send the form fields defined in the request</summary>
		/// <param name="request">The http request object</param>
		/// <param name="template">The xsl template used to transform the xml to html</param>
		public void Send(HttpRequest request, XslCompiledTransform template) {
			message = formatMessage(request, template);
			Send();
		}
		/// <summary>Send the form fields defined in the request</summary>
		/// <param name="request">The http request object</param>
		/// <param name="fldformat">The fld format type to be used</param>
		public void Send(HttpRequest request, x_emailfields fldformat) {
			message = formatMessage(request, fldformat);
			Send();
		}
		/// <summary>Send the (transformed) xml document</summary>
		/// <param name="msgxml">The http request object</param>
		/// <param name="template">The xsl template used to transform the xml to html</param>
		public void Send(XmlDocument msgxml, XslCompiledTransform template) {
			message = formatMessage(msgxml, template);
			Send();
		}
		/// <summary>Send the (transformed) xml element</summary>
		/// <param name="msgxml">The message xml</param>
		/// <param name="template">The xsl template used to transform the xml to html</param>
		public void Send(XmlElement msgxml, XslCompiledTransform template) {
			message = formatMessage(msgxml, template);
			Send();
		}
		/// <summary>Send the (transformed) xml document with attachments</summary>
		/// <param name="msgxml">The http request object</param>
		/// <param name="template">The xsl template used to transform the xml to html</param>
		/// <param name="attachments">Concatenated list of attachment files (separated by '|')</param>
		public void Send(XmlDocument msgxml, XslCompiledTransform template, string attachments) {
			message = formatMessage(msgxml, template);
			MailMessage thisMail = new MailMessage();
			char[] delim = new char[] { '|' };
			foreach (string attachment in attachments.Split(delim)) {
				if (attachment != null && attachment != "") {
					thisMail.Attachments.Add(new Attachment(attachment));
				}
			}
			Send(thisMail);
		}
		/// <summary>Sends the xml using the xslt template</summary>
		/// <param name="xmlelem">The xml element</param>
		/// <param name="template">The xsl template</param>
		/// <param name="logfile">The file (full path) to write as a log</param>
		public void Send(XmlElement xmlelem, XslCompiledTransform template, string logfile) {
			type = x_emailtype.html;	// asume transform to html for now
			message = formatMessage(xmlelem, template);
			StreamWriter sr = File.CreateText(logfile);
			sr.Write(message);
			sr.Close();
			Send();
		}
		/// <summary>Sends the xml using the xslt template</summary>
		/// <param name="xmlelem">The xml element</param>
		/// <param name="template">The xsl template</param>
		/// <param name="arguments">The xsl template arguments</param>
		/// <param name="logfile">The file (full path) to write as a log</param>
		public void Send(XmlElement xmlelem, XslCompiledTransform template, XsltArgumentList arguments, string logfile) {
			type = x_emailtype.html;	// asume transform to html for now
			message = formatMessage(xmlelem, template, arguments);
			StreamWriter sr = File.CreateText(logfile);
			sr.Write(message);
			sr.Close();
			Send();
		}
		#endregion

		#region Private sending methods (from BlogX.x_email)
		/// <overloads>Format the message body for form submissions</overloads>
		/// <summary>Formats to xml or transformed to html</summary>
		/// <param name="request">The http request object</param>
		/// <param name="xslt">The xsl template</param>
		private string formatMessage(HttpRequest request, XslCompiledTransform xslt) {
			XmlDocument msgdoc = new XmlDocument();
			string msg = (type == x_emailtype.text) ? "Form fields:" : "<form/>";
			if (type == x_emailtype.xml || type == x_emailtype.html)
				msgdoc.LoadXml(msg);
			String[] fldnames = request.Form.AllKeys;
			foreach (string fldname in fldnames) {
				if (type == x_emailtype.text)
					msg = String.Concat(msg, "\n", fldname, ":\t", request.Form[fldname]);
				else {
					XmlElement fld = (XmlElement)msgdoc.DocumentElement.AppendChild(msgdoc.CreateElement("field"));
					fld.SetAttribute("name", fldname);
					fld.InnerText = request.Form[fldname];
				}
			}
			if (type != x_emailtype.text)
				msg = formatMessage(msgdoc, xslt);
			return msg;
		}
		/// <summary>Formats to xml or transformed to html</summary>
		/// <param name="msgdoc">The xml document</param>
		/// <param name="xslt">The xsl template</param>
		private string formatMessage(XmlDocument msgdoc, XslCompiledTransform xslt) {
			return formatMessage(msgdoc.DocumentElement, xslt);
		}
		/// <summary>Formats to xml or transformed to html</summary>
		/// <param name="msgel">The xml element</param>
		/// <param name="xslt">The xsl template</param>
		private string formatMessage(XmlElement msgel, XslCompiledTransform xslt) {
			return formatMessage(msgel, xslt, (XsltArgumentList)null);
		}
		/// <summary>Formats to xml or transformed to html</summary>
		/// <param name="msgel">The xml element</param>
		/// <param name="xslt">The xsl template</param>
		/// <param name="alttext">The alternative text of the message</param>
		private string formatMessage(XmlElement msgel, XslCompiledTransform xslt, string alttext) {
			altmessage = alttext;
			return formatMessage(msgel, xslt, (XsltArgumentList)null);
		}
		/// <summary>Formats to xml or transformed to html</summary>
		/// <param name="msgel">The xml element</param>
		/// <param name="xslt">The xsl template</param>
		/// <param name="args">The xsl template arguments</param>
		private string formatMessage(XmlElement msgel, XslCompiledTransform xslt, XsltArgumentList args) {
			string msg = "";
			if (type == x_emailtype.xml)
				msg = msgel.OuterXml;
			else if (type == x_emailtype.html) {
				msg = transformMessage(msgel, xslt, args);
			} else
				msg = msgel.InnerText;
			return msg;
		}
		/// <summary>Formats to delimited text</summary>
		private string formatMessage(HttpRequest request, x_emailfields format) {
			string delim, msg = "";
			switch (format) {
				case x_emailfields.semicolon: delim = ";"; break;
				case x_emailfields.space: delim = " "; break;
				case x_emailfields.comma:
				default: delim = ","; break;
			}
			String[] fldnames = request.Form.AllKeys;
			foreach (string fldname in fldnames) {
				msg = String.Concat(msg, request.Form[fldname], delim);
			}
			return msg;
		}
		private string transformMessage(XmlElement msgel, XslCompiledTransform xslt) {
			return transformMessage(msgel, xslt, null);
		}
		private string transformMessage(XmlElement msgel, XslCompiledTransform xslt, XsltArgumentList args) {
			StringWriter writer = new StringWriter();
			xslt.Transform(msgel, args, writer);
			return writer.ToString();
		}
		#endregion

		#region	Public sending methods (from original EmailX.x_email - includes sending via raw smtp)
		/// <overloads>Sends a text email</overloads>
		/// <summary>
		/// Assumes to, from and subject already set
		/// </summary>
		/// <param name="message">The email message</param>
		public void SendText(string message) {
			Message = message;
			send(false);
		}
		/// <summary>
		/// Uses the to, from, subject and message supplied.
		/// </summary>
		/// <param name="to">The destination email address</param>
		/// <param name="from">The originating email address</param>
		/// <param name="subject">The email subject</param>
		/// <param name="message">The email message</param>
		public void SendText(string to, string from, string subject, string message) {
			To = to;
			From = from;
			Subject = subject;
			Message = message;
			send(false);
		}
		/// <summary>
		/// Uses the to, from, subject, message and filepath supplied.
		/// </summary>
		/// <param name="to">The destination email address</param>
		/// <param name="from">The originating email address</param>
		/// <param name="subject">The email subject</param>
		/// <param name="message">The email message</param>
		/// <param name="filepath">The fully qualified path of the file to attatch</param>
		public void SendText(string to, string from, string subject, string message, string filepath) {
			To = to;
			From = from;
			Subject = subject;
			Message = message;
			send(false, filepath);
		}
		
		/// <overloads>Sends a html email</overloads>
		/// <summary>
		/// Assumes to, from and subject already set
		/// </summary>
		/// <param name="message">The email message</param>
		public void SendHtml(string message) {
			Message = message;
			send(true);
		}
		/// <summary>
		/// Uses the to, from, subject and message supplied.
		/// </summary>
		/// <param name="to">The destination email address</param>
		/// <param name="from">The originating email address</param>
		/// <param name="subject">The email subject</param>
		/// <param name="message">The email message</param>
		public void SendHtml(string to, string from, string subject, string message) {
			To = to;
			From = from;
			Subject = subject;
			Message = message;
			send(true);
		}
		/// <summary>
		/// Uses the to, from, subject and message supplied.
		/// </summary>
		/// <param name="to">The destination email address</param>
		/// <param name="from">The originating email address</param>
		/// <param name="subject">The email subject</param>
		/// <param name="message">The email message</param>
		/// <param name="filepath">The fully qualified path of the file to attatch</param>
		public void SendHtml(string to, string from, string subject, string message, string filepath) {
			To = to;
			From = from;
			Subject = subject;
			Message = message;
			send(true, filepath);
		}
		/// <summary>
		/// Sends using a list of xml nodes. using supplied from, subject and message for all emails.
		/// </summary>
		/// <param name="emailList">An Xml document that contains a list of nodes containing email addresses</param>
		/// <param name="emailNodeName">The name of the elements in the list that have an 'email' attribute</param>
		/// <param name="from">The originating email address</param>
		/// <param name="subject">The email subject</param>
		/// <param name="message">The email message</param>
		public void SendHtml(XmlDocument emailList, string emailNodeName, string from, string subject, string message) {
			Message = message;
			Subject = subject;
			XmlNodeList emailNodeList = emailList.SelectNodes(string.Concat("//", emailNodeName));
			From = from;
			foreach (XmlNode emailNode in emailNodeList) {
				XmlElement emailElem = (XmlElement) emailNode;
				try {
					ToCheck = emailElem.GetAttribute("email");
					send(true);
					//sendraw();	// works OK but not for html
					emailElem.SetAttribute("send", "OK");
					emailElem.SetAttribute("send_message", "Success");
				} catch(Exception e) {
					emailElem.SetAttribute("send", "NotOK");
					emailElem.SetAttribute("send_message", e.Message);
				}
			}
		}
		
		
		/// <overloads>Sends a mail message to a SMTP server using 'raw' smtp commands</overloads>
		/// <summary>
		/// Sends the message using the default settings in the web.config file
		/// </summary>
		/// <param name="message">The email message</param>
		public void SendRaw(string message) {
			Message = message;
			sendraw();
		}
		/// <summary>
		/// Sends the message using the default settings in the web.config file, with the specified login details
		/// </summary>
		/// <param name="message">The email message</param>
		/// <param name="username">The login username</param>
		/// <param name="password">The login password</param>
		public void SendRaw(string message, string username, string password) {
			Message = message;
			sendraw(username, password);
		}
		/// <summary>
		/// Sends the message using default settings in the web.config file, except to, from and subject
		/// </summary>
		/// <param name="to">The destination email address</param>
		/// <param name="from">The originating email address</param>
		/// <param name="subject">The email subject</param>
		/// <param name="message">The email message</param>
		public void SendRaw(string to, string from, string subject, string message) {
			To = to;
			From = from;
			Subject = subject;
			Message = message;
			sendraw();
		}
		/// <summary>
		/// Sends the message using default settings in the web.config file, except to, from and subject, with the specified login details
		/// </summary>
		/// <param name="to">The destination email address</param>
		/// <param name="from">The originating email address</param>
		/// <param name="subject">The email subject</param>
		/// <param name="message">The email message</param>
		/// <param name="username">The login username</param>
		/// <param name="password">The login password</param>
		public void SendRaw(string to, string from, string subject, string message, string username, string password) {
			To = to;
			From = from;
			Subject = subject;
			Message = message;
			sendraw(username, password);
		}
		#endregion

		#region Private methods (from original EmailX.x_email)
		/// <overloads>Sends the mail using the SmtpMail class</overloads>
		/// <summary>
		/// Sends a basic, text email
		/// </summary>
		private void send() {
			send(false);
		}
		/// <summary>
		/// Sends a basic, text or html email
		/// </summary>
		private void send(bool isHtml) {
			MailMessage thismsg = new MailMessage();
			setMessage(thismsg, isHtml);
			setSmtp().Send(thismsg);
		}
		/// <summary>
		/// Sends a basic, text or html email with a delimited list of file attatchments
		/// </summary>
		private void send(bool isHtml, string attachments) {
			MailMessage thismsg = new MailMessage();
			setMessage(thismsg, isHtml);
			char[] delim = new char[] { '|' };
			foreach (string attachment in attachments.Split(delim)) {
				if (attachment != null && attachment != "") {
					thismsg.Attachments.Add(new Attachment(attachment));
				}
			}
			setSmtp().Send(thismsg);
		}
		/// <overloads>Sends the mail using the x_smtp class</overloads>
		/// <summary>
		/// Uses the specified username/password
		/// </summary>
		/// <param name="username">The username</param>
		/// <param name="password">The password</param>
		private void sendraw(string username, string password) {
			x_smtp thismail = new x_smtp();
			thismail.SendMail(From, To, Subject, Message, username, password, Server);
		}
		/// <summary>
		/// Uses the default username/password
		/// </summary>
		private void sendraw() {
			x_smtp thismail = new x_smtp();
			string username = config.SmtpUsername;
			string password = config.SmtpPassword;
			thismail.SendMail(From, To, Subject, Message, username, password, Server);
		}
		/// <summary>
		/// Sets the common message parameters
		/// </summary>
		private void setMessage(MailMessage msg, bool isHtml) {
			msg.To.Add(new MailAddress(to));
			msg.From = new MailAddress(from);
			if (cc != null && cc != "") {
				msg.CC.Add(new MailAddress(cc));
			}
			if (bcc != null && bcc != "") {
				msg.Bcc.Add(new MailAddress(bcc));
			}
			msg.Subject = subject;
			msg.Body = message;
			msg.IsBodyHtml = isHtml;
		}
		#endregion
		
		#region Protected methods
		#endregion
				
		#region Private methods
		/// <summary>
		/// Initalise all defaults
		/// </summary>
		private void initialise() {
			config = new x_config();
			type = (config.Type == null)? DEFAULT_TYPE : getType(config.Type);
			to = (config.To == null)? DEFAULT_TO : config.To;
			from = (config.From == null)? DEFAULT_FROM : config.From;
			subject = (config.Subject == null)? DEFAULT_SUBJECT : config.Subject;
			server = (config.Host == null)? DEFAULT_SERVER : config.Host;
			bcc = (config.Bcc == null)? DEFAULT_BCC : config.Bcc;
		}
    	// returns true if email is in valid e-mail format.
		private string isValidEmail(string email) {
    		if (Regex.IsMatch(email, @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"))
				return email;
			else
				throw(new Exception("bad email format"));
		}
		private SmtpClient setSmtp() {
			SmtpClient smtp = new SmtpClient();
			if (server == null || server == "")	// if if not explicitly set, use config
				server = config.Host;
			if (server != null && server != "")
				smtp.Host = server;
			smtp.Credentials = CredentialCache.DefaultNetworkCredentials;
			return smtp;
		}
		/// <summary>
		/// Gets the email type from a string
		/// </summary>
		/// <param name="type">The string representation of the email type</param>
		/// <returns></returns>
		private x_emailtype getType(string type) {
			x_emailtype resultType;
			switch (type) {
				case "html":
					resultType = x_emailtype.html;
					break;
				case "xml":
					resultType = x_emailtype.xml;
					break;
				case "text":
				default:
					resultType = x_emailtype.text;
					break;
			}
			return resultType;
		}
		#endregion
	}
}
